/*
File:		Emitter.cpp
Author:		Skyler Suzuki
Purpose:	CEmitter class handles the Flyweight pattern 
			and particles that belong to the emitter
*/

#include "Emitter.h"
#include "../GameObjects/Entity.h"
#include "Particle.h"
#include "../../SGD_Wrappers/CSGD_TextureManager.h"
#include "../../SGD_Wrappers/SGD_Math.h"
#include <ctime>
#include <algorithm>
#include <cmath>

CEmitter::CEmitter(void)
{
	m_eEmitterShape		= EMITTER_RECT;
	m_bIsLooping		= false;
	m_fVelocityStartX	= 0.0f;
	m_fVelocityEndX		= 0.0f;
	m_fVelocityStartY	= 0.0f;
	m_fVelocityEndY		= 0.0f;
	m_fScaleStart		= 0.0f;
	m_fScaleEnd			= 0.0f;
	m_fEmissionRate		= 0.0f;
	m_fSpawnTime		= 0.0f;
	m_fRotationStart	= 0.0f;
	m_fRotationEnd		= 0.0f;
	m_nMinLife			= 0;
	m_nMaxLife			= 0;
	m_colorEnd.pCA		= 255;
	m_colorEnd.pCR		= 255;
	m_colorEnd.pCG		= 255;
	m_colorEnd.pCB		= 255;
	m_colorStart.pCA	= 255;
	m_colorStart.pCR	= 255;
	m_colorStart.pCG	= 255;
	m_colorStart.pCB	= 255;
	m_nShape			= 0;
	m_nMaxParticles		= 0;	
	m_nImageID			= -1;

	m_unRefCount		= 1;
	//m_entityPosParticle	= nullptr;
}


CEmitter::~CEmitter(void)
{
	std::list<CParticle*>::iterator it;
	std::list<CParticle*>::iterator dit;

	for(it = m_listLiveParticles.begin(); it != m_listLiveParticles.end(); ++it)
	{
		delete (*it);
	}
	m_listLiveParticles.clear();

	for(dit = m_listDeadParticles.begin(); dit != m_listDeadParticles.end(); ++dit)
	{
		delete (*dit);
	}
	m_listDeadParticles.clear();
}

void CEmitter::Explosion()
{
	for(int i = 0; i < m_nMaxParticles; i++)
	{
		m_listLiveParticles.push_back(GenerateParticle());
	}
}

bool CEmitter::Update(float fElapsedTime)
{
	int count = m_listLiveParticles.size() - 1;

	if(m_bIsLooping)
	{
		if(m_listLiveParticles.size() < (unsigned int)m_nMaxParticles)
		{
			if(m_fSpawnTime > m_fEmissionRate)
			{
				m_listLiveParticles.push_back(GenerateParticle());
				m_fSpawnTime = 0;
			}		
			m_fSpawnTime += fElapsedTime;			
		}
	}
	else if(!m_bIsLooping)
	{
		if(count < 0)
		{
			Explosion();
		}
	}

	std::list<CParticle*>::iterator it;
	
	for(it = m_listLiveParticles.begin(); it != m_listLiveParticles.end(); ++it)
	{
		float timeAlive = (*it)->GetCurrLife() / (*it)->GetMaxLife();
		
		if( timeAlive > 1.0f )
			timeAlive = 1.0f;
		else if( timeAlive < 0 )
			timeAlive = 0;

		(*it)->SetCurrPositionX((*it)->GetCurrPositionX() + 
			(*it)->GetCurrVelocity().fX * (((m_fVelocityEndX - m_fVelocityStartX) * timeAlive) + m_fVelocityStartX));
		
		(*it)->SetCurrPositionY((*it)->GetCurrPositionY() + 
			(*it)->GetCurrVelocity().fY * (((m_fVelocityEndY - m_fVelocityStartY) * timeAlive) + m_fVelocityStartX));


		(*it)->SetCurrRotation(
			m_fRotationStart + ((m_fRotationEnd - m_fRotationStart) * timeAlive) * (SGD_PI / 180.0f));

		(*it)->SetCurrScale(
			m_fScaleStart + ((m_fScaleEnd - m_fScaleStart) * timeAlive));

		(*it)->SetParticleColor(
			(int)(m_colorStart.pCA + (m_colorEnd.pCA - m_colorStart.pCA) * timeAlive),
			(int)(m_colorStart.pCR + (m_colorEnd.pCR - m_colorStart.pCR) * timeAlive),
			(int)(m_colorStart.pCG + (m_colorEnd.pCG - m_colorStart.pCG) * timeAlive),
			(int)(m_colorStart.pCB + (m_colorEnd.pCB - m_colorStart.pCB) * timeAlive));


		if((*it)->GetCurrLife() > (*it)->GetMaxLife())
		{
			m_listDeadParticles.push_back(*it);
			m_listLiveParticles.remove(*it);
			if(count <= 0 && !m_bIsLooping)
			{
				return false;
			}
			break;
		}
		(*it)->SetCurrLife((*it)->GetCurrLife() + fElapsedTime);		
	}
	return true;
}

void CEmitter::Render(void)
{
	CSGD_TextureManager* pTM = CSGD_TextureManager::GetInstance();
	RECT rSource = { 0, 0, 64, 64 };

	std::list<CParticle*>::iterator it;
	
	for(it = m_listLiveParticles.begin(); it != m_listLiveParticles.end(); ++it)
	{
		pTM->Draw(m_nImageID, (int)(*it)->GetCurrPositionX(), (int)(*it)->GetCurrPositionY(), 
			(*it)->GetCurrScale(), (*it)->GetCurrScale(),
			&rSource, (float)rSource.right / 2, (float)rSource.bottom / 2, (*it)->GetCurrRotation(),
			D3DCOLOR_ARGB((*it)->GetCurrParticlColor().pCA, (*it)->GetCurrParticlColor().pCR,
			(*it)->GetCurrParticlColor().pCG, (*it)->GetCurrParticlColor().pCB));
	}
}

CParticle* CEmitter::GenerateParticle(void)
{
	CParticle* p = new CParticle();

	int maxLife = rand() % m_nMaxLife + m_nMinLife;

	p->SetCurrLife(0);
	 
	p->SetMaxLife((float)maxLife);
	//p->SetCurrPosition(m_entityPosParticle->Getposition())
	RECT rect = { 100, 100, 164, 164 };

	if(m_eEmitterShape == EMITTER_RECT)
	{
		int posX = rand() % (rect.right - rect.left) + rect.left;
		int posY = rand() % (rect.bottom - rect.top) + rect.top;
		p->SetCurrPositionX((float)posX);
		p->SetCurrPositionY((float)posY);
	}

	else if(m_eEmitterShape == EMITTER_CIRCLE)
	{
		POINT midCircle = { rect.left + ((rect.right - rect.left) / 2), 
			rect.top + ((rect.bottom - rect.top) / 2) };
		float radius = 32.0f;

		float randomNum = (float)((double) rand() / (RAND_MAX) * radius);

		float rotation = rand() % 628 / 100.0f;
		float vX = (float)cos(rotation);
		float vY = (float)sin(rotation);

		p->SetCurrPositionX( midCircle.x + vX * randomNum );
		p->SetCurrPositionY( midCircle.x + vX * randomNum );
	}
	else if(m_eEmitterShape == EMITTER_POINT)
	{
		p->SetCurrPositionX(p->GetCurrPositionX());
		p->SetCurrPositionY(p->GetCurrPositionY());		
	}

	if(!m_bIsLooping)
	{
		float rotation = rand() % 628 / 100.0f;
		p->SetCurrVelocity(cos(rotation), sin(rotation));
	}
	else
	{
		float currVX, currVY;
		if( m_fVelocityStartX == 0.0f && m_fVelocityEndX == 0.0f)
		{
			currVX = 0.0f;
		}
		else
		{
			currVX = m_fVelocityStartX + (((rand() % 100) / 100) * (m_fVelocityEndX / m_fVelocityStartX));
		}
		
		if( m_fVelocityStartY == 0.0f && m_fVelocityEndY == 0.0f)
		{
			currVY = 0.0f;
		}
		else
		{	
			currVY = m_fVelocityStartY + (((rand() % 100) / 100) * (m_fVelocityEndY / m_fVelocityStartY));
		}

		p->SetCurrVelocity(currVX, currVY);
	}
	  
	p->SetCurrScale(m_fScaleStart);
	 
	p->SetCurrRotation(m_fRotationStart);
	 
	p->SetParticleColor(m_colorStart.pCA, m_colorStart.pCR, m_colorStart.pCG, m_colorStart.pCB);

	return p;
}

void CEmitter::AddRef( void )
{
	++m_unRefCount;
}

void CEmitter::Release( void )
{
	--m_unRefCount;

	if( m_unRefCount == 0 )
		delete this;
}

#pragma region Mutators
void CEmitter::SetEmitterShape(EmitterShape eEmitterShape)
{	 
	m_eEmitterShape = eEmitterShape;
}	 
	 
void CEmitter::SetIsLooping(bool bIsLooping)
{	
	m_bIsLooping = bIsLooping;
}	 
	 
void CEmitter::SetVelocityStartX(float fVelocityStartX)
{	 
	m_fVelocityStartX = fVelocityStartX;
}	 
	 
void CEmitter::SetVelocityEndX(float fVelocityEndX)
{	 
	m_fVelocityEndX = fVelocityEndX;
}

void CEmitter::SetVelocityStartY(float fVelocityStartY)
{
	m_fVelocityStartY = fVelocityStartY;
}

void CEmitter::SetVelocityEndY(float fVelocityEndY)
{
	m_fVelocityEndY = fVelocityEndY;
}

void CEmitter::SetScaleStart(float fScaleStart)
{	 
	m_fScaleStart = fScaleStart;
}	 
	 
void CEmitter::SetScaleEnd(float fScaleEnd)
{	 
	m_fScaleEnd = fScaleEnd;
}	 
	 
void CEmitter::SetEmissionRate(float fEmissionRate)
{	
	m_fEmissionRate = fEmissionRate;
}	 
	 
void CEmitter::SetRotationStart(float fRotationStart)
{	
	m_fRotationStart = fRotationStart;
}	 
	 
void CEmitter::SetRotationEnd(float fRotationEnd)
{	
	m_fRotationEnd = fRotationEnd;
}	 
	 
void CEmitter::SetMinLife(int nMinLife)
{	 
	m_nMinLife = nMinLife;
}	 
	 
void CEmitter::SetMaxLife(int nMaxLife)
{	 
	m_nMaxLife = nMaxLife;
}

void CEmitter::SetStartColor(particleColor colorStart)
{
	m_colorStart = colorStart;
}

void CEmitter::SetEndColor(particleColor colorEnd)
{
	m_colorEnd = colorEnd;
}
	 
void CEmitter::SetShape(int nShape)
{	
	m_nShape = nShape;
}	 
	 
void CEmitter::SetMaxParticles(int nMaxParticles)
{	 
	m_nMaxParticles = nMaxParticles;
}	 
	 
void CEmitter::SetImageID(int nImageID)
{	 
	m_nImageID = nImageID;
}	 
	 
void CEmitter::SetParticleName(std::string sParticleName)
{	 
	m_sParticleName = sParticleName;
}

void CEmitter::SetFileName(std::string sFileName)
{	 
	m_sFileName = sFileName;
}
	 
void CEmitter::SetEntityPosParticle(CEntity *entityPosParticle)
{
	m_entityPosParticle = entityPosParticle;
}
#pragma endregion

#pragma region Accessors
EmitterShape CEmitter::GetEmitterShape(void) const
{
	return m_eEmitterShape;
}			 

bool CEmitter::GetIsLooping(void) const
{
	return m_bIsLooping;
}

float CEmitter::GetVelocityStartX(void) const
{
	return m_fVelocityStartX;
}

float CEmitter::GetVelocityEndX(void) const
{
	return m_fVelocityEndX;
}

float CEmitter::GetVelocityStartY(void) const
{
	return m_fVelocityStartY;
}

float CEmitter::GetVelocityEndY(void) const
{
	return m_fVelocityEndY;
}

float CEmitter::GetScaleStart(void) const
{
	return m_fScaleStart;
}

float CEmitter::GetScaleEnd(void) const
{
	return m_fScaleEnd;
}

float CEmitter::GetEmissionRate(void) const
{
	return m_fEmissionRate;
}

float CEmitter::GetRotationStart(void) const
{
	return m_fRotationStart;
}

float CEmitter::GetRotationEnd(void) const
{
	return m_fRotationEnd;
}

int CEmitter::GetMinLife(void) const
{
	return m_nMinLife;
}

int CEmitter::GetMaxLife(void) const
{
	return m_nMaxLife;
}

particleColor CEmitter::GetStartColor(void) const
{
	return m_colorStart;
}

particleColor CEmitter::GetEndColor(void) const
{
	return m_colorEnd;
}

int CEmitter::GetShape(void) const
{
	return m_nShape;
}

int CEmitter::GetMaxParticles(void) const
{
	return m_nMaxParticles;
}

int CEmitter::GetImageID(void) const
{
	return m_nImageID;
}

std::string CEmitter::GetParticleName(void) const
{
	return m_sParticleName;
}

std::string CEmitter::GetFileName(void) const
{
	return m_sFileName;
}

CEntity*	CEmitter::GetEntityPosParticle(void) const
{
	return m_entityPosParticle;
}
#pragma endregion
